home *** CD-ROM | disk | FTP | other *** search
/ One Click 14 / OneClick14.iso / Ferramentas / Convert XLS to Pdf / xls2pdf_setup.exe / {app} / lib / pdfopt.ps < prev    next >
Encoding:
Text File  |  2002-06-02  |  33.5 KB  |  1,155 lines

  1. %    Copyright (C) 2000, 2001 Aladdin Enterprises.  All rights reserved.
  2. % This software is provided AS-IS with no warranty, either express or
  3. % implied.
  4. % This software is distributed under license and may not be copied,
  5. % modified or distributed except as expressly authorized under the terms
  6. % of the license contained in the file LICENSE in this distribution.
  7. % For more information about licensing, please refer to
  8. % http://www.ghostscript.com/licensing/. For information on
  9. % commercial licensing, go to http://www.artifex.com/licensing/ or
  10. % contact Artifex Software, Inc., 101 Lucas Valley Road #110,
  11. % San Rafael, CA  94903, U.S.A., +1(415)492-9861.
  12.  
  13. % $Id: pdfopt.ps,v 1.14 2002/06/02 12:03:28 mpsuzuki Exp $
  14. % PDF linearizer ("optimizer").
  15.  
  16. .currentglobal true .setglobal
  17. /pdfoptdict 200 dict def
  18. pdfoptdict begin
  19.  
  20. % This linearizer is designed for simplicity, not for performance.
  21. % See the main program (the last procedure in the file) for comments
  22. % describing the main processing sequence.
  23.  
  24. % ---------------- Utilities ---------------- %
  25.  
  26. % ------ Data structures ------ %
  27.  
  28. % Distinguish dictionaries, arrays, and everything else.
  29. /ifdaelse {        % <obj> <dictproc> <arrayproc> <otherproc> ifdaelse -
  30.   3 index type dup /dicttype eq {
  31.     pop pop pop
  32.   } {
  33.     dup /arraytype ne exch /packedarraytype ne and {
  34.       exch
  35.     } if pop exch pop
  36.   } ifelse exec
  37. } bind def
  38.  
  39. % Implement dynamically growable arrays using a dictionary.
  40. /darray {        % <size> darray <darray>
  41.   dict
  42. } bind def
  43. /dadd {            % <darray> <value> dadd -
  44.   1 index length exch put
  45. } bind def
  46. /daforall {        % <darray> <proc> daforall -
  47.   /exch cvx /get cvx 3 -1 roll /exec cvx 5 packedarray cvx
  48.   0 1 2 index 0 get length 1 sub 4 -1 roll for
  49. } bind def
  50. /dacontents {        % <darray> dacontents <array>
  51.   [ exch { } daforall ]
  52. } bind def
  53. /dacontstring {        % <darray> dacontstring <string>
  54.   0 1 index { exch pop length add } forall string
  55.   dup /NullEncode filter
  56.             % Stack: darray str filter
  57.   3 -1 roll { 1 index exch writestring } daforall
  58.   closefile
  59. } bind def
  60.  
  61. % Force an object, mapping it if it is a reference.
  62. /omforcenew {        % <obj> omforce <obj'> <notseen>
  63.   dup oforce 2 copy eq { pop true } { exch 0 get omapnew exch pop } ifelse
  64. } bind def
  65. /omforce {        % <obj> omforce <obj'>
  66.   omforcenew pop
  67. } bind def
  68. /omget {        % <dict|array> <key> omget <obj>
  69.   get omforce
  70. } bind def
  71. % Visit an entire tree.
  72. /omvisit {        % <obj> omvisit -
  73.   omforcenew {
  74.     { { omvisit omvisit } forall }
  75.     { { omvisit } forall }
  76.     { pop }
  77.     ifdaelse
  78.   } {
  79.     pop
  80.   } ifelse
  81. } bind def
  82. % Visit a tree, stopping at references to Page objects.
  83. % (This is only needed for the OpenAction in the Catalog.)
  84. /omvisitnopage {    % <obj> omvisitnopage -
  85.   dup oforce dup type /dicttype eq {
  86.     /Type .knownget { /Page eq } { false } ifelse
  87.   } {
  88.     pop false
  89.   } ifelse {
  90.     pop        % Page reference
  91.   } {
  92.     omforcenew {
  93.       { { omvisitnopage omvisitnopage } forall }
  94.       { { omvisitnopage } forall }
  95.       { pop }
  96.       ifdaelse
  97.     } {
  98.       pop
  99.     } ifelse
  100.   } ifelse
  101. } bind def
  102.  
  103. % Collect the list of currently mapped object numbers, in order.
  104. /omapped {        % - omapped <obj#s>
  105.   RMap length array RMap {
  106.     2 index 3 1 roll 1 sub exch put
  107.   } forall
  108. } bind def
  109.  
  110. % Collect the list of object numbers passed to omap by a procedure.
  111. /visited {        % <proc> visited <obj#s>
  112.   false currentomap 2 .execn
  113.   omapped exch setomap
  114. } bind def
  115.  
  116. % ------ Output ------ %
  117.  
  118. % Provide a framework for closure-based streams.
  119. .currentglobal false .setglobal
  120. userdict /clostreams 20 dict put    % stream -> [data endproc]
  121. .setglobal
  122. % Create a closure-based stream.
  123. /clostream {        % <data> <proc> <endproc> clostream <stream>
  124.   2 index 3 -1 roll /exec load 3 packedarray cvx
  125.   /NullEncode filter
  126.         % Stack: data endproc stream
  127.   clostreams 1 index 5 -2 roll 2 array astore put
  128. } bind def
  129. % Close a closure-based stream.
  130. /closend {        % <stream> closend <result>
  131.   dup closefile clostreams exch
  132.   2 copy get 3 1 roll undef aload pop exec
  133. } bind def
  134.  
  135. % Implement in-memory output streams.
  136. /msproc {        % <data> <more> <accum> msproc <scratch>
  137.   3 -1 roll dadd { 100 string } { () } ifelse
  138. } bind def
  139. /mstream {        % - mstream <mstream>
  140.   10 darray {msproc} {dacontstring} clostream
  141. } bind def
  142. /mcontents {        % <mstream> mcontents <string>
  143.   closend
  144. } bind def
  145.  
  146. % Implement a stream that only keeps track of its position.
  147. % (All streams should do this, but the PLRM doesn't require it.)
  148. /posbuf 100 string def
  149. /posproc {        % <data> <more> <accum> posproc <scratch>
  150.   0 2 copy get 5 -1 roll length add put
  151.   pop //posbuf
  152. } bind def
  153. /postream {        % - postream <postream>
  154.   [0] {posproc} {0 get} clostream
  155. } bind def
  156. /poslength {        % <postream> poslength <pos>
  157.   closend
  158. } bind def
  159.  
  160. % Implement streams with variable-bit-width data.
  161. % Note that these are dictionary objects, not stream objects.
  162. /bitstream {        % <stream> bitstream <bstream>
  163.   4 dict begin /S exch def /N 8 def /B 0 def
  164.   currentdict end
  165. } bind def
  166. /bitwrite {        % <bstream> <value> <width> bitwrite -
  167.   PDEBUG { ( ) print 1 index =only (:) print dup = } if
  168.   3 -1 roll begin
  169.   N exch sub dup 0 ge {
  170.     /N exch def N bitshift B add
  171.   } {
  172.     2 copy bitshift B add S exch write
  173.             % Stack: value -left
  174.     { 8 add dup 0 ge { exit } if
  175.       2 copy bitshift 255 and S exch write
  176.     } loop
  177.     /N 1 index def bitshift 255 and
  178.   } ifelse /B exch def
  179.   end
  180. } bind def
  181. /bitflush {        % <bstream> bitflush -
  182.   begin N 8 ne { S B write /B 0 def /N 8 def } if end
  183. } bind def
  184.  
  185. % Capture OFile output on the temporary file, in memory, or just as a length.
  186. /totemp {        % <proc> totemp <start> <end>
  187.   TFile fileposition OFile
  188.   /OFile TFile def 3 .execn
  189.   /OFile exch def
  190.   TFile fileposition
  191. } bind def
  192. /tomemory {        % <proc> tomemory <string>
  193.   OFile /OFile mstream def 2 .execn
  194.   OFile mcontents exch /OFile exch def
  195. } bind def
  196. /tolength {        % <proc> tolength <string>
  197.   OFile /OFile postream def 2 .execn
  198.   OFile poslength exch /OFile exch def
  199. } bind def
  200.  
  201. % Copy a range of bytes from TFile to OFile.
  202. /copyrange {        % <start> <end> copybytes -
  203.   TFile 2 index setfileposition
  204.   exch sub 1024 string exch {
  205.         % Stack: buf left
  206.     2 copy 1 index length .min 0 exch getinterval
  207.     TFile exch readstring pop OFile exch writestring
  208.     1 index length sub dup 0 le { exit } if
  209.   } loop pop pop
  210. } bind def
  211.  
  212. % Pad with blanks to a specified position.
  213. /padto {        % <pos> padto -
  214.   OFile fileposition sub
  215.   dup 0 lt {
  216.     (ERROR: file position incorrect by ) print =
  217.     /padto cvx /rangecheck signalerror
  218.   } {
  219.     { ( ) ows } repeat
  220.   } ifelse
  221. } bind def
  222.  
  223. % ---------------- Read objects into memory ---------------- %
  224.  
  225. /touch {        % <object> touch -
  226.   {
  227.     { touch touch } forall
  228.   } {
  229.     dup xcheck {
  230.         % Executable array, must be an indirect object.
  231.       dup 0 get resolved? { pop pop } { oforce touch } ifelse
  232.     } {
  233.       { touch } forall
  234.     } ifelse
  235.   } {
  236.     pop
  237.   } ifdaelse
  238. } bind def
  239.  
  240. % ---------------- Replace references with referents ---------------- %
  241.  
  242. /replaceable? {        % <value> replaceable? <bool>
  243.   dup type /integertype eq exch xcheck not and
  244. } bind def
  245. /replacement {        % <obj|ref> replacement <obj'>
  246.   dup oforce dup replaceable? { exch } if pop
  247. } bind def
  248.  
  249. /replacerefs {        % <object> replacerefs <object>
  250.   {
  251.     dup {
  252.       2 index 2 index undef
  253.       exch replacement exch replacement
  254.       2 index 3 1 roll put
  255.     } forall
  256.   } {
  257.     0 1 2 index length 1 sub {
  258.       1 index exch 2 copy get replacement put
  259.     } for
  260.   } {
  261.   } ifdaelse
  262. } bind def
  263.  
  264. /replaceReferences {    % - replaceReferences -
  265.   Objects { replacerefs pop } lforall
  266.         % Delete replaced objects.
  267.   0 1 Objects llength 1 sub {
  268.     Objects 1 index lget replaceable? {
  269.       PDEBUG { (Deleting ) print dup = } if
  270.       Generations 1 index 0 lput
  271.     } if pop
  272.   } for
  273. } bind def
  274.  
  275. % ---------------- Create new objects ---------------- %
  276.  
  277. /createObjects {    % [<obj>...] createObjects <firstobj#>
  278.   Objects llength dup
  279.   dup 3 index length add growPDFobjects
  280.         % Stack: objects objn objn
  281.   3 1 roll exch {
  282.     Objects 2 index 3 -1 roll lput
  283.     Generations 1 index 1 lput
  284.     1 add
  285.   } forall pop
  286. } bind def
  287.  
  288. % ---------------- Propagate attributes ---------------- %
  289.  
  290. /nopropattrs <<
  291.     % Never propagate these.
  292.   /Type dup /Kids dup /Count dup /Parent dup
  293.     % Handle Resources specially.
  294.   /Resources dup
  295. >> def
  296.  
  297. % Merge Resources.
  298. /mergeres {        % <fromdict> <todict> mergeres -
  299.         % Values in todict take priority over fromdict.
  300.   1 index /Resources .knownget {
  301.     1 index /Resources .knownget {
  302.         % Stack: fromdict todict fromres tores
  303.       exch oforce exch oforce
  304.         % todict's Resources may be shared, so make a copy.
  305.       dup length dict .copydict
  306.       exch {
  307.         % Stack: fromdict todict tores' fromkey fromvalue
  308.     2 index 2 index knownoget {
  309.         % Stack: fromdict todict tores' fromkey fromvalue tovalue
  310.       exch oforce exch
  311.         % ProcSet is an array, other types are dictionaries.
  312.       dup type /dicttype eq {
  313.         % Dictionary, not ProcSet.
  314.         exch dup length 2 index length add dict .copydict .copydict
  315.       } {
  316.         % Array or packed array, ProcSet.
  317.         % Use dictionaries to do the merge.
  318.         dup length 2 index length add dict begin
  319.         exch { dup def } forall { dup def } forall
  320.         mark currentdict end { pop } forall .packtomark
  321.       } ifelse
  322.     } if
  323.     2 index 3 1 roll put
  324.       } forall
  325.     } if /Resources exch put pop
  326.   } {
  327.     pop pop
  328.   } ifelse
  329. } bind def
  330.  
  331. % Merge attributes other than Resources.
  332. /mergeattrs {        % <fromdict> <todict> mergeattrs <fromdict> <todict>
  333.         % Values in todict take priority over fromdict.
  334.   1 index {
  335.         % Stack: fromdict todict fromkey fromvalue
  336.     //nopropattrs 2 index known {
  337.       pop pop
  338.     } {
  339.       2 index 2 index known { pop pop } { 2 index 3 1 roll put } ifelse
  340.     } ifelse
  341.   } forall
  342. } bind def
  343.  
  344. % Propagate attributes to a subtree.
  345. /proppage {        % <attrs> <subtree> proppage -
  346.         % We should be able to tell when we reach a leaf
  347.         % by finding a Type unequal to /Pages.  Unfortunately,
  348.         % some files distributed by Adobe lack the Type key
  349.         % in some of the Pages nodes!  Instead, we check for Kids.
  350.   dup /Kids knownoget {
  351.         % Accumulate inherited values.
  352.     3 1 roll
  353.         % Stack: kids attrs pagesnode
  354.     dup length dict .copydict mergeattrs
  355.     dup 3 1 roll mergeres
  356.     exch { oforce 1 index exch proppage } forall pop
  357.   } {
  358.         % Merge inherited values into the leaf.
  359.     mergeattrs mergeres
  360.   } ifelse
  361. } bind def
  362.  
  363. % Propagate attributes to all pages.
  364. /propagateAttributes {    % - propagateAttributes -
  365.   0 dict Trailer /Root oget /Pages oget proppage
  366. } bind def
  367.  
  368. % ---------------- Identify document-level objects ---------------- %
  369.  
  370. /identifyDocumentObjects {    % - identifyDocumentObjects <obj#s>
  371.   {
  372.     Trailer /Root omget
  373.     dup /PageMode .knownget { omvisit } if
  374.     % Don't allow omvisit to trace references to Page objects.
  375.     dup /OpenAction .knownget { omvisitnopage } if
  376.     Trailer /Encrypt .knownget { omvisit } if
  377.     dup /Threads .knownget {
  378.       omforce { omforce } forall
  379.     } if
  380.     dup /AcroForm .knownget { omvisit } if
  381.     pop
  382.   } visited
  383. } bind def
  384.  
  385. % ---------------- Identify the objects of each page ---------------- %
  386.  
  387. /identifyfont {        % <fontref> identifyfont -
  388.   omforce {
  389.     exch /FontDescriptor eq {
  390.       omforce dup /Flags .knownget { 32 and 0 ne } { false } ifelse
  391.       exch {
  392.     exch dup dup /FontFile eq exch /FontFile2 eq or
  393.     exch /FontFile3 eq or 2 index and {
  394.       fontfiles exch dadd
  395.     } {
  396.       omvisit
  397.     } ifelse
  398.       } forall pop
  399.     } {
  400.       omvisit
  401.     } ifelse
  402.   } forall
  403. } bind def
  404.  
  405. % Collect all the objects referenced from a page.  The first object number
  406. % (which may not be the smallest one) is that of the page object itself.
  407. /identifyPageObjects {    % <extra> <page#> identifyPageObjects <obj#s>
  408.   PDEBUG {
  409.     (%Objects for page: ) print dup =
  410.   } if
  411.   pdffindpageref
  412.   dup 0 get 3 1 roll
  413.   4 dict begin
  414.   /images 10 darray def
  415.   /fontfiles 10 darray def
  416.   {
  417.     omforce
  418.         % Stack: pageobj# extra page
  419.         % Visit any extra objects if applicable.
  420.     exch omvisit
  421.         % Visit Annots, if any.
  422.         % We don't try to defer the drawing information.
  423.     dup /Annots .knownget { omvisit } if
  424.         % Visit beads.
  425.     dup /B .knownget { omvisit } if
  426.         % Visit resources dictionaries.
  427.     dup /Resources .knownget {
  428.       omforce dup {
  429.         % Visit the first-level Resource dictionaries.
  430.     omforce pop pop
  431.       } forall {
  432.         % Visit the resources themselves.
  433.         % Skip Image XObjects, and FontFile streams if the
  434.         % FontDescriptor Flags have bit 6 set.
  435.         % We don't try to visit the resources in the order in which
  436.         % the Contents stream(s) reference(s) them.
  437.     exch dup /XObject eq {
  438.       pop oforce {
  439.         dup oforce /Subtype get /Image eq {
  440.           images exch dadd
  441.         } {
  442.           omvisit
  443.         } ifelse pop
  444.       } forall
  445.     } {
  446.       /Font eq {
  447.         oforce { identifyfont pop } forall
  448.       } {
  449.         oforce omvisit
  450.       } ifelse
  451.     } ifelse
  452.       } forall
  453.     } if
  454.         % Visit the Contents stream(s).
  455.     dup /Contents .knownget { omvisit } if
  456.         % Visit Image XObjects.  We don't try to visit them in
  457.         % reference order.
  458.     images { omvisit } daforall
  459.         % Visit FontFile streams.  We don't try to visit them in
  460.         % reference order.
  461.     fontfiles { omvisit } daforall
  462.     pop
  463.   } visited end
  464.         % Stack: pageobj# obj#s
  465.   [ 3 1 roll {
  466.     2 copy eq { pop } { exch } ifelse
  467.   } forall counttomark 1 roll ]
  468.   PDEBUG {
  469.     (%Objects = ) print dup === flush
  470.   } if
  471. } bind def
  472.  
  473. % Identify the objects of the first page.
  474. /identifyFirstPageObjects {    % - identifyFirstPageObjects <obj#s>
  475.   Trailer /Root oget null
  476.   1 index /PageMode knownoget {
  477.     /UseOutlines eq {
  478.       1 index /Outlines knownoget { exch pop } if
  479.     } if
  480.   } if exch pop
  481.   1 identifyPageObjects
  482. } bind def
  483.  
  484. % Identify the non-shared objects of the other pages, and the shared objects.
  485. % Note that the page objects themselves may appear to be shared, because of
  486. % references from Dest entries in annotations, but they must be treated as
  487. % non-shared.  Note also that some objects referenced on the first page may
  488. % also be referenced from other pages.
  489. /identifyOtherPageObjects {    % - identifyOtherPageObjects [<pageobj#s> ...]
  490.                 %   <sharedobj#s>
  491.   4 dict begin
  492.   /marks lstring Objects llength lgrowto def
  493.         % Collect objects of other pages and identify sharing.
  494.   [ 2 1 pdfpagecount { null exch identifyPageObjects } for ]
  495.   dup {
  496.     { marks exch 2 copy lget 1 add 254 .min lput } forall
  497.   } forall
  498.         % Mark document-level and first page objects.
  499.   [CatalogNs FirstPageNs] {
  500.     { marks exch 255 lput } forall
  501.   } forall
  502.         % Mark the page objects themselves as non-shared.
  503.   dup {
  504.     0 get marks exch 1 lput
  505.   } forall
  506.         % Collect the non-shared objects of each page.
  507.   [ exch {
  508.     [ exch {
  509.       marks 1 index lget 1 ne { pop } if
  510.     } forall ]
  511.   } forall ]
  512.         % Collect the shared objects.
  513.   [ 1 1 marks llength 1 sub {
  514.     marks 1 index lget dup 1 le exch 255 eq or { pop } if
  515.   } for ]
  516.   end
  517. } bind def
  518.  
  519. % Identify objects not associated with any page.
  520. /identifyNonPageObjects {    % - identifyNonPageObjects <obj#s>
  521.   4 dict begin
  522.   /marks lstring Objects llength lgrowto def
  523.   [[[LPDictN PHSN] CatalogNs FirstPageNs SharedNs] OtherPageNs] {
  524.     { { marks exch 1 lput } forall } forall
  525.   } forall
  526.     %****** PUT THESE IN A REASONABLE ORDER ******
  527.   [ 1 1 Objects llength 1 sub {
  528.     marks 1 index lget 0 eq {
  529.       Generations 1 index lget 0 eq { pop } if
  530.     } {
  531.       pop
  532.     } ifelse
  533.   } for ]
  534. } bind def
  535.  
  536. % ---------------- Assign object numbers ---------------- %
  537.  
  538. % Assign object numbers to all objects that will be copied.
  539. % Return the first (translated) object number in the First Page xref table.
  540. /assignObjectNumbers {        % - assignObjectNumbers -
  541.   OtherPageNs { { omap pop } forall } forall
  542.   SharedNs { omap pop } forall
  543.   NonPageNs { omap pop } forall
  544.         % Assign object numbers for the First Page xref table last.
  545.   LPDictN omap    % don't pop, this is the return value
  546.   CatalogNs { omap pop } forall
  547.   FirstPageNs { omap pop } forall
  548.   PHSN omap pop
  549. } bind def
  550.  
  551. % ---------------- Create the LPDict ---------------- %
  552.  
  553. % Create the contents of the LPDict.
  554. /createLPDict {            % <phsstart> <phsend> <firstpageend>
  555.                 %   <xref0start> <filelength> createLPDict -
  556.   LPDict
  557.   dup /Linearized 1 put
  558.   dup /L 4 -1 roll put        % filelength
  559.   dup /T 4 -1 roll put        % xref0start
  560.   dup /E 4 -1 roll put        % firstpageend
  561.   dup /H 5 -2 roll 1 index sub 2 array astore put    % phsstart, end-start
  562.   dup /O 1 pdffindpageref 0 get omap put
  563.   /N pdfpagecount put
  564. } bind def
  565.  
  566. % ---------------- Adjust object positions ---------------- %
  567.  
  568. /adjustObjectPositions {    % <boundary> <deltabelow> <deltaabove>
  569.                 %   adjustObjectPositions -
  570.     % Objects fall into 4 categories: LPDict, PHS, Catalog, and others.
  571.     % We handle the first two as special cases.
  572.   XRef {
  573.         % Stack: bdy below above key loc
  574.     dup 5 index ge { 2 } { 3 } ifelse index add
  575.     XRef 3 1 roll put
  576.   } forall pop pop pop
  577.   XRef LPDictN omap HeaderLength put
  578.   XRef PHSN omap PHSStart put
  579. } bind def
  580.  
  581. % ---------------- Write the output file ---------------- %
  582.  
  583. % Write objects identified by object number.
  584. /writeobjn {        % <obj#> writeobjn -
  585.   Generations 1 index lget pdfwriteobj
  586. } bind def
  587. /writeobjns {        % <obj#s> writeobjns -
  588.   { writeobjn } forall
  589. } bind def
  590.  
  591. % Write a part of the output file.
  592. /writePart {        % <proc> <label> writePart -
  593.   PDEBUG {
  594.     dup print ( count=) print count =only ( start=) print
  595.     OFile { .fileposition } stopped { pop (???) } if =
  596.     2 .execn
  597.     print ( end=) print
  598.     OFile { .fileposition } stopped { pop (???) } if =
  599.   } {
  600.     pop exec
  601.   } ifelse
  602. } bind def
  603.  
  604. % Write the header.
  605. /writePart1 {        % - writePart1 -
  606.   {
  607.     pdfwriteheader
  608.   } (part1) writePart
  609. } bind def
  610.  
  611. % Write the linearization parameters dictionary.
  612. /writePart2 {        % - writePart2 -
  613.   {
  614.     LPDictN writeobjn
  615.   } (part2) writePart
  616. } bind def
  617.  
  618. % Write the First Page xref table and trailer.
  619. % Free variables: FirstPageXN.
  620. /writePart3 {        % <xrefstart> writePart3 -
  621.   {
  622.     FirstPageXN NObjects 1 add 1 index sub pdfwritexref
  623.     Trailer dup length 1 add dict copy
  624.     dup /Size NObjects 1 add put
  625.     dup /Prev 4 -1 roll put
  626.     pdfwritetrailer
  627.     0 pdfwritestartxref
  628.   } (part3) writePart
  629. } bind def
  630.  
  631. % Write the Catalog and other required document-level objects.
  632. % Free variables: CatalogNs.
  633. /writePart4 {        % - writePart4 -
  634.   {
  635.     CatalogNs writeobjns
  636.   } (part4) writePart
  637. } bind def
  638.  
  639. % Write the Primary Hint Stream.
  640. /writePart5 {        % - writePart5 -
  641.   {
  642.     PHSN writeobjn
  643.   } (part5) writePart
  644. } bind def
  645.  
  646. % Write the First Page's objects.
  647. % Free variables: FirstPageNs.
  648. /writePart6 {        % - writePart6 -
  649.   {
  650.     FirstPageNs writeobjns
  651.   } (part6) writePart
  652. } bind def
  653.  
  654. % Write the objects of other pages (Page + non-shared objects).
  655. % Free variables: OtherPageNs.
  656. /writePart7 {        % - writePart7 <lengths>
  657.   {
  658.     [ OtherPageNs {
  659.       OFile fileposition exch
  660.       writeobjns OFile fileposition exch sub
  661.     } forall ]
  662.   } (part7) writePart
  663. } bind def
  664.  
  665. % Write the shared objects of other pages.
  666. % Free variables: SharedNs.
  667. /writePart8 {        % - writePart8 -
  668.   {
  669.     SharedNs writeobjns
  670.   } (part8) writePart
  671. } bind def
  672.  
  673. % Write the other objects not associated with pages.
  674. % Free variables: NonPageNs.
  675. /writePart9 {        % - writePart9 -
  676.   {
  677.     NonPageNs writeobjns
  678.   } (part9) writePart
  679. } bind def
  680.  
  681. % Write the main xref table and trailer.
  682. % Free variables: FirstPageXN.
  683. /writePart11xref {    % writePart11 -
  684.   {
  685.     0 FirstPageXN pdfwritexref
  686.   } (part11xref) writePart
  687. } bind def
  688. /writePart11rest {    % <part3start> writePart11rest -
  689.   {
  690.     << /Size FirstPageXN >> pdfwritetrailer
  691.     pdfwritestartxref
  692.   } (part11rest) writePart
  693. } bind def
  694.  
  695. % ---------------- Write hint tables ---------------- %
  696.  
  697. /bitsneeded {        % <maxvalue> bitsneeded <#bits>
  698.   0 exch { dup 0 eq { pop exit } if exch 1 add exch 2 idiv } loop
  699. } bind def
  700.  
  701. % Find the start and end of objects in the output.
  702. /omstart {        % <obj#> omstart <pos>
  703.   PDEBUG { (start\() print dup =only } if
  704.   omap
  705.   PDEBUG { (=>) print dup =only } if
  706.   XRef exch get
  707.   PDEBUG { (\) = ) print dup = } if
  708. } bind def
  709. /omend {        % <obj#> omend <pos>
  710.     % The end of an object is the start of the next object.
  711.     % The caller must be sure that this object is not the last one
  712.     % in part 9.
  713.   PDEBUG { (end\() print dup =only } if
  714.   omap
  715.   PDEBUG { (=>) print dup =only } if
  716.   1 add
  717.     % Check that the requested object wasn't the last one in part 6:
  718.     % the next object in the output file is the first in part 7.
  719.   PHSN omap 1 index eq { pop 1 } if
  720.   XRef exch get
  721.   PDEBUG { (\) = ) print dup = } if
  722. } bind def
  723. /omlength {        % <obj#> omlength <length>
  724.   dup omend exch omstart sub
  725. } bind def
  726.  
  727. % Find the Contents of a page.
  728. /contentsobjects { % <pagedict> contentsobjects <firstobj#> <lastobj#> true
  729.            % <pagedict> contentsobjects false
  730.   /Contents .knownget {
  731.     dup oforce dup type /dicttype eq {
  732.       pop dup
  733.     } {
  734.       dup 0 get exch dup length 1 sub get
  735.     } ifelse
  736.     exch 0 get exch 0 get true
  737.   } {
  738.     false
  739.   } ifelse
  740. } bind def
  741. /contentsstart {    % <pagedict> contentsstart <pos> true
  742.             % <pagedict> contentsstart false
  743.   contentsobjects { pop omstart true } { false } ifelse
  744. } bind def
  745. /contentslength {    % <pagedict> contentslength <length>
  746.   contentsobjects { omend exch omstart sub } { 0 } ifelse
  747. } bind def
  748.  
  749.  
  750. /writePageOffsetHints {
  751.   PDEBUG { /writePageOffsetHints == } if
  752.   20 dict begin
  753.   /bits OFile bitstream def
  754.   /bwn { bits 3 1 roll bitwrite } def
  755.  
  756.     % Calculate least length of a page.
  757.   FirstPageLength OtherPageLengths { .min } forall
  758.   /minpl exch def
  759.     % Calculate least contents length.
  760.   FirstPageNs 0 get Objects exch lget contentslength
  761.   OtherPageNs { 0 get Objects exch lget contentslength .min } forall
  762.   /mincl exch def
  763.  
  764.     % The Adobe documentation says that all versions of Acrobat
  765.     % require item 8 (mincl) to be zero.  Patch this here.
  766.   /mincl 0 def
  767.  
  768.     % Calculate bits needed to represent greatest page length.
  769.   FirstPageLength OtherPageLengths { .max } forall
  770.   minpl sub bitsneeded /maxplbits exch def
  771.     % Calculate bits needed to represent the greatest Contents length.
  772.   FirstPageNs 0 get Objects exch lget contentslength
  773.   OtherPageNs { 0 get Objects exch lget contentslength .max } forall
  774.   mincl sub bitsneeded /maxclbits exch def
  775.  
  776.     % Per Adobe documentation, Acrobat requires that item 5 (maxplbits)
  777.     % be equal to item 9 (maxclbits).  Set both to the max of the two.
  778.   maxplbits maxclbits .max /maxplbits 1 index def /maxclbits exch def
  779.  
  780.         % 1: Least number of objects in a page:
  781.   FirstPageNs length OtherPageNs { length .min } forall
  782.   /minnop 1 index def 32 bwn
  783.         % 2: Location of first page's Page object:
  784.   FirstPageNs 0 get omap XRef exch get 32 bwn
  785.         % 3: Bits needed to represent greatest # of objects in a page:
  786.   FirstPageNs length OtherPageNs { length .max } forall
  787.   minnop sub bitsneeded /maxnopbits 1 index def 16 bwn
  788.         % 4: Least length of a page:
  789.   minpl 32 bwn
  790.         % 5: Bits needed to represent the greatest page length:
  791.   maxplbits 16 bwn
  792.         % 6: Least start of Contents offset:
  793.   0        % (Acrobat requires that this be 0.)
  794.   /minsco 1 index def 32 bwn
  795.         % 7: Bits needed to represent the greatest start of Contents
  796.         % offset:
  797.   0        % (Acrobat ignores this.)
  798.   /maxscobits 1 index def 16 bwn
  799.         % 8: Least contents length:
  800.   mincl 32 bwn
  801.         % 9: Bits needed to represent the greatest Contents length:
  802.   maxclbits 16 bwn
  803.         % 10: Bits needed to represent the greatest number of Shared
  804.   0        % Object references (we don't report any):
  805.   /maxsorbits 1 index def 16 bwn
  806.         % 11: Bits needed to identify a Shared Object (we don't
  807.   0        % report any):
  808.   /sobits 1 index def 16 bwn
  809.         % 12: Bits needed to represent numerator of fraction (only
  810.   0        % needed for Shared Object references, which we don't report):
  811.   /numfbits 1 index def 16 bwn
  812.         % 13: Denominator of fraction (only needed for Shared Object
  813.         % references, which we don't report):
  814.   255    % arbitrary
  815.   /denf 1 index def 16 bwn
  816.  
  817.         % Number of objects in pages:
  818.   FirstPageNs length minnop sub maxnopbits bwn
  819.   OtherPageNs {
  820.     length minnop sub maxnopbits bwn
  821.   } forall
  822.  
  823.         % Total length of pages in bytes;
  824.   FirstPageLength minpl sub maxplbits bwn
  825.   OtherPageLengths {
  826.     minpl sub maxplbits bwn
  827.   } forall
  828.  
  829.         % Number of shared objects referenced from page:
  830.         % (Currently we don't report this.)
  831.   OtherPageNs length 1 add { 0 maxsorbits bwn } repeat
  832.  
  833.         % Since there are no shared object references,
  834.         % the next two sections are empty.
  835.  
  836.         % Contents offsets:
  837.   [FirstPageNs OtherPageNs aload pop] {
  838.     0 get Objects exch lget contentsstart { minsco sub } { 0 } ifelse
  839.     maxscobits bwn
  840.   } forall
  841.  
  842.         % Contents lengths:
  843.   [FirstPageNs OtherPageNs aload pop] {
  844.     0 get Objects exch lget contentslength mincl sub maxclbits bwn
  845.   } forall
  846.  
  847.   bits bitflush end
  848. } bind def
  849.  
  850. /writeSharedObjectHints {
  851.   PDEBUG { /writeSharedObjectHints == } if
  852.   20 dict begin
  853.   /bits OFile bitstream def
  854.   /bwn { bits 3 1 roll bitwrite } def
  855.  
  856.         % Currently we use the Shared Object hint table only for
  857.         % the objects in the first page, which are all treated as
  858.         % "shared" objects.
  859.  
  860.         % Object number of first object in Shared Objects section
  861.         % (not currently used):
  862.   0 32 bwn
  863.         % Location of first object in Shared Objects section
  864.         % (not currently used): If there are no shared objects,
  865.         % Acrobat sets this to the location of linearization
  866.         % parameters object (the very first object).
  867.   { pdfwriteheader } tomemory length 32 bwn
  868.         % Number of Shared Object entries for first page:
  869.   FirstPageNs length 32 bwn
  870.         % Number of Shared Object entries for Shared Objects
  871.         % section (not currently used):
  872.   FirstPageNs length 32 bwn
  873.         % Bits needed to represent the greatest number of objects
  874.         % in a shared object group (always 0, because all groups
  875.         % have only 1 object):
  876.   0 16 bwn
  877.         % Least length of a Shared Object Group in bytes:
  878.   16#7fffffff FirstPageNs { omlength .min } forall
  879.   /minsol 1 index def 32 bwn
  880.         % Bits needed to represent the greatest length of a
  881.         % Shared Object Group:
  882.   0 FirstPageNs { omlength .max } forall
  883.   minsol sub bitsneeded
  884.   /maxsolbits 1 index def 16 bwn
  885.  
  886.         % Lengths of shared object groups:
  887.   FirstPageNs { omlength minsol sub maxsolbits bwn } forall
  888.  
  889.         % MD5 flag:
  890.   0 1 bwn
  891.  
  892.   bits bitflush end
  893. } bind def
  894.  
  895. % ---------------- Main program ---------------- %
  896.  
  897. /pdfOptimize {        % <infile> <outfile> pdfOptimize -
  898.   realtime 3 1 roll
  899.   exch pdfdict begin pdfopenfile dup begin
  900.   40 dict begin
  901.   /IDict exch def
  902.   /OFile exch def
  903.   /starttime exch def
  904.   /now {
  905.     QUIET { pop } { print (, t = ) print realtime starttime sub = flush } ifelse
  906.   } def
  907.   omapinit
  908.   
  909.     % Create and open a temporary file.
  910.     % Because of .setsafe, we have to open it as read-write, rather than
  911.     % opening for writing, then closing it and reopening it for reading.
  912.  
  913.   null (w+) .tempfile /TFile exch def /TFileName exch def
  914.   .setsafe
  915.  
  916.     % Read all objects into memory.
  917.  
  918.   Trailer touch
  919.   (Read objects) now
  920.  
  921.     % Replace indirect references to numbers.  This is needed
  922.     % for the Length of streams, and doesn't hurt anything else.
  923.  
  924.   replaceReferences
  925.   (Replaced references) now
  926.  
  927.     % Create the two new objects: the linearization parameter
  928.     % dictionary, and the Primary Hint Stream.
  929.  
  930.   /LPDict 10 dict def
  931.   /PHS 10 dict cvx def        % executable = stream
  932.   [LPDict PHS] createObjects
  933.   /LPDictN 1 index def 1 add
  934.   /PHSN exch def
  935.   PDEBUG { << /LPDictN LPDictN /PHSN PHSN >> === } if
  936.  
  937.     % Count the number of objects in the output.
  938.  
  939.   0 0 1 Objects llength 1 sub {
  940.     Generations exch lget 0 ne { 1 add } if
  941.   } for
  942.   /NObjects exch def
  943.   QUIET not { NObjects =only ( objects total) = flush } if
  944.  
  945.     % Propagate inherited attributes down the page tree.
  946.  
  947.   propagateAttributes
  948.   (Propagated attributes) now
  949.  
  950.     % Identify the document-level objects (part 4).
  951.  
  952.   identifyDocumentObjects /CatalogNs exch def
  953.   QUIET not { CatalogNs === flush } if
  954.   (Identified Catalog) now
  955.  
  956.       % Identify the first page's objects (part 6),
  957.     % including the Outlines tree if appropriate.
  958.  
  959.   pdfopencache
  960.   /FirstPageNs identifyFirstPageObjects def
  961.   QUIET not { FirstPageNs === flush } if
  962.   (Identified first page) now
  963.  
  964.     % Identify shared vs. non-shared objects for remaining pages
  965.     % (parts 7 and 8).
  966.  
  967.   identifyOtherPageObjects
  968.   /SharedNs exch def
  969.   /OtherPageNs exch def
  970.   QUIET not { OtherPageNs === flush SharedNs === flush } if
  971.   (Identified other pages) now
  972.  
  973.     % Identify objects not associated with any page (part 9).
  974.  
  975.   /NonPageNs identifyNonPageObjects def
  976.   QUIET not { NonPageNs === flush } if
  977.   (Identified non-pages) now
  978.  
  979.     % Assign final object numbers to all the objects.
  980.     % (The omap is currently empty.)
  981.  
  982.   /FirstPageXN assignObjectNumbers def
  983.   (Assigned objects #s) now
  984.  
  985.     % Write the document-level objects (part 4).
  986.  
  987.   { writePart4 } totemp
  988.   /CatalogTempEnd exch def /CatalogTempStart exch def
  989.   (Wrote Catalog) now
  990.  
  991.     % Write the first page's objects (part 6).
  992.  
  993.   { writePart6 } totemp
  994.   /FirstPageTempEnd exch def /FirstPageTempStart exch def
  995.   (Wrote first page) now
  996.  
  997.     % Write the non-shared objects for other pages (part 7).
  998.  
  999.   { writePart7 /OtherPageLengths exch def } totemp
  1000.   /OtherPageTempEnd exch def /OtherPageTempStart exch def
  1001.   (Wrote other pages) now
  1002.  
  1003.     % Write the shared objects for other pages (part 8).
  1004.  
  1005.   { writePart8 } totemp
  1006.   /SharedTempEnd exch def /SharedTempStart exch def
  1007.   (Wrote shared objects) now
  1008.  
  1009.     % Write the objects not associated with pages (part 9).
  1010.  
  1011.   { writePart9 } totemp
  1012.   /NonPageTempEnd exch def /NonPageTempStart exch def
  1013.  
  1014.     % Compute conservative lengths of parts 2,3,5,11 of the output.
  1015.     % It's OK for these to be too large, but not too small.
  1016.  
  1017.   % Make dummy XRef entres for LPDict and PHS.
  1018.   XRef LPDictN omap 0 put
  1019.   XRef PHSN omap 0 put
  1020.  
  1021.   /HeaderLength {    % this is exact
  1022.     writePart1            % part 1
  1023.   } tolength def
  1024.   /CatalogLength    % this is exact
  1025.     CatalogTempEnd CatalogTempStart sub def    % part 4
  1026.   /FirstPageLength    % this is exact
  1027.     FirstPageTempEnd FirstPageTempStart sub def    % part 6
  1028.   /OtherObjectsLength    % this is exact
  1029.     NonPageTempEnd OtherPageTempStart sub def    % parts 7,8,9
  1030.   /ObjectsLength    % this is exact
  1031.     CatalogLength FirstPageLength add OtherObjectsLength add def
  1032.   /XrefLength {            % part 11
  1033.     % The LPDict must end within the first 1024 bytes,
  1034.     % so the start of the FirstPage xref table can't exceed 1024.
  1035.     writePart11xref 1024 writePart11rest
  1036.   } tolength def
  1037.   /NominalFileLength     % Make a generous allowance for parts 2,3,5.
  1038.     HeaderLength ObjectsLength 3 mul add 10000 add 99999 .max def
  1039.   /FirstPageXrefLength {    % part 3
  1040.     NominalFileLength writePart3
  1041.   } tolength def
  1042.   /LPDictLength {        % part 2
  1043.     NominalFileLength dup 2 mul 2 copy add 1 index dup createLPDict writePart2
  1044.   } tolength def
  1045.  
  1046.     % Compute a few additional values from the above.
  1047.  
  1048.   /XrefBeginLength {
  1049.     (xref\n0 ) ows
  1050.     OFile FirstPageXN write=
  1051.   } tolength def
  1052.   HeaderLength LPDictLength add
  1053.   /FirstPageXrefStart 1 index def
  1054.   FirstPageXrefLength add
  1055.   /CatalogStart 1 index def
  1056.   CatalogLength add        % phsstart
  1057.   /PHSStart exch def
  1058.  
  1059.     % Adjust the object positions ignoring PHS.
  1060.     % (Writing the PHS needs these.)
  1061.  
  1062.   0 0 CatalogStart CatalogTempStart sub adjustObjectPositions
  1063.   % Make a temporary XRef entry for the PHS, for the benefit of omend.
  1064.   XRef PHSN omap CatalogStart put
  1065.   (Adjusted positions) now
  1066.  
  1067.     % Construct the hint tables (part 5).
  1068.  
  1069.   { writePageOffsetHints } totemp
  1070.   pop /PHSTempStart exch def
  1071.   { writeSharedObjectHints } totemp
  1072.   exch PHSTempStart sub PHS /S 3 -1 roll put
  1073.   PHSTempStart sub /PHSTempLength exch def
  1074.   (Wrote hints) now
  1075.  
  1076.   % Prepare to read TFile.
  1077. %  TFile closefile
  1078. %  /TFile TFileName (r) file def
  1079.  
  1080.   PHS
  1081.     dup /File TFile put
  1082.     dup /FilePosition PHSTempStart put
  1083.     dup /Length PHSTempLength put
  1084.   pop
  1085.   /PHSLength { writePart5 } tolength def
  1086.  
  1087.     % Construct the linearization parameter dictionary (part 2).
  1088.  
  1089.   PHSStart
  1090.   dup PHSLength add        % phsend
  1091.   /FirstPageStart 1 index def
  1092.   dup FirstPageLength add    % firstpageend
  1093.   dup OtherObjectsLength add
  1094.   /XrefStart 1 index def
  1095.   XrefBeginLength add        % xref0start
  1096.   dup XrefBeginLength sub XrefLength add    % fileend
  1097.     % Because of a bug, Acrobat Reader doesn't recognize any file
  1098.     % shorter than 1K as linearized.  Patch this here.
  1099.   1024 .max
  1100.   /FileLength 1 index def
  1101.   createLPDict
  1102.  
  1103.     % Adjust the object positions again, taking the PHS into account.
  1104.  
  1105.   PHSStart 0 PHSLength adjustObjectPositions
  1106.   (Readjusted positions) now
  1107.  
  1108.     % Finally, write the output file.
  1109.  
  1110.   writePart1
  1111.   writePart2
  1112.   FirstPageXrefStart padto
  1113.   XrefStart writePart3
  1114.   CatalogStart padto
  1115.   CatalogTempStart CatalogTempEnd copyrange    % part 4
  1116.   writePart5
  1117.   FirstPageStart padto
  1118.   FirstPageTempStart NonPageTempEnd copyrange    % parts 6,7,8,9
  1119.   % No Overflow Hint Stream (part 10).
  1120.   XrefStart padto
  1121.   writePart11xref
  1122.   { FirstPageXrefStart writePart11rest } tomemory
  1123.   FileLength 1 index length sub padto ows
  1124.   (Wrote output file) now
  1125.  
  1126.     % Wrap up.
  1127.  
  1128.   TFile closefile TFileName deletefile
  1129.   end        % temporary dict
  1130.   end        % IDict
  1131. } bind def
  1132.  
  1133. end            % pdfoptdict
  1134. .setglobal
  1135.  
  1136. % Check for command line arguments.
  1137. [ shellarguments {
  1138.   ] dup length 2 eq {
  1139.     % Load the pdfwrite utilities if necessary.
  1140.     /omapinit where { pop } { (pdfwrite.ps) runlibfile } ifelse
  1141.     save exch
  1142.     aload pop exch (r) file exch (w) file
  1143.     3000000 setvmthreshold
  1144.     pdfoptdict begin pdfOptimize end
  1145.     restore
  1146.   } {
  1147.     (Usage: gs -dNODISPLAY -- pdfopt.ps input.pdf output.pdf) = flush quit
  1148.   } ifelse
  1149. } {
  1150.   pop
  1151. } ifelse
  1152.